//-WinterFramework © WinterFeather
using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using DG.Tweening;
using DG;
using System.Collections.Generic;
using System.Reflection;
using Unity.Burst;
using Unity.Mathematics;
using System.Runtime.CompilerServices;
public class SingleMonoScene<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance
    {
        get
        {
            if (!instance)
            {
                GameObject g = new GameObject(typeof(T).Name);
                instance = GameObject.FindObjectOfType<T>();
                if (!instance) instance = g.AddComponent<T>();
                //g.hideFlags = HideFlags.HideInHierarchy;
            }
            return instance;
        }
    }
    static T instance;

    public static bool IsDestory()
    {
        return instance == null;
    }
}
public class SingleMono<T> : MonoBehaviour where T : MonoBehaviour
{
    public void Awake()
    {
        GameObject.DontDestroyOnLoad(gameObject);
    }

    public static T Instance
    {
        get
        {
            if (!instance && !isDestory)
            {
                GameObject g = new GameObject(typeof(T).ToString());
                //GameObject.DontDestroyOnLoad(g);
                instance = GameObject.FindObjectOfType<T>();
                if (!instance) instance = g.AddComponent<T>();
                //g.hideFlags = HideFlags.HideInHierarchy;
            }
            return instance;
        }
    }
    static T instance;

    static bool isDestory = false;

    void OnApplicationQuit()
    {
        isDestory = true;
    }

}
public class SingleData<T> where T : class, new()
{
    static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new T();
            }
            return instance;
        }
    }
}
public static class BinarySerializationHelper
{
    public static byte[] SerializeToBinary(object obj)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, obj);
            return ms.ToArray();
        }
    }

    public static T DeserializeFromBinary<T>(byte[] data)
    {
        using (MemoryStream ms = new MemoryStream(data))
        {
            BinaryFormatter bf = new BinaryFormatter();
            return (T)bf.Deserialize(ms);
        }
    }
}

public class DebugHelper
{

}

public class BehaviourPropertys
{
    private PropertyID[] propertyIDs;
    public BehaviourPropertys()
    {
        var count = Enum.GetValues(typeof(PropertyEnum)).Length;
        propertyIDs = new PropertyID[count];
        for (int i = 0; i < count; i++)
        {
            propertyIDs[i] = new PropertyID(0);
        }
    }
    public void SetBase(PropertyEnum id, float v)
    {
        
        propertyIDs[(int)id] = propertyIDs[(int)id].Base(v);// = new PropertyID(v); 
    }
    public void Add(PropertyEnum id, float v)
    {
        propertyIDs[(int)id] = propertyIDs[(int)id].Add(v);
    }
    public void Mul(PropertyEnum id, float v)
    {
        propertyIDs[(int)id] = propertyIDs[(int)id].Mul(v);
    }
    public float Value(PropertyEnum id) => propertyIDs[(int)id].Value;

    public void Clear()
    {
        for (int i = 0; i < propertyIDs.Length; i++)
        {
            propertyIDs[i] = new PropertyID(0);
        }
    }
}

public static class BehaviourPropertysHelper
{
    private static readonly Dictionary<Type, List<Mapping>> Cache = new();

    private class Mapping
    {
        public PropertyEnum id;
        public float value;
    }

    public static void InitFrom<T>(this BehaviourPropertys bag, T src)
    {
        var type = typeof(T);
        if (!Cache.TryGetValue(type, out var map))
        {
            map = new List<Mapping>();
            foreach (var f in type.GetFields())
            {

                if (f.FieldType != typeof(float)) continue;

                var attr = f.GetCustomAttribute<PropAttribute>();
                if (attr != null)
                {
                    Debug.Log($"[{type.Name}] {f.Name} = {f.GetValue(src)}");
                    map.Add(new Mapping { id = attr.Id, value = (float)f.GetValue(src) });
                    continue;
                }

            }
            Cache[type] = map;
        }
        foreach (var m in map)
        {
            Debug.Log($"init from {type.Name} id:{m.id} value:{m.value}");
            bag.SetBase(m.id, m.value);
        }
    }
}

public enum Layer { Base, Equipment, Buff }  
[BurstCompile]
public readonly struct PropertyID
{
    //private readonly PropLayer[] layers;
    private readonly float baseValue;
    private readonly float add;
    private readonly float mul;
    public float Value => math.max(0f, baseValue * (1f + mul)) + add;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public PropertyID(float b) : this(b, 0f, 0f) { }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private PropertyID(float b, float a, float m)
    {
        baseValue = b;
        add = a;
        mul = math.max(-1f, m);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public PropertyID Add(float v) => new PropertyID(baseValue, add + v, mul);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public PropertyID Mul(float v) => new PropertyID(baseValue, add, mul + v);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public PropertyID Base(float v) => new PropertyID(v, add, mul);
}
[BurstCompile]
public readonly struct PropLayer
{
    public readonly float add;
    public readonly float mul;
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public PropLayer(float a = 0, float m = 0) { add = a; mul = m; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public PropLayer Add(float v) => new PropLayer(add + v, mul);
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public PropLayer Mul(float v) => new PropLayer(add, mul + v);
}


// public enum PropertyEnum
// {
//     Hp,
//     Atk,
//     AtkSpeedMulti,
//     Range,
//     Speed
// }

[AttributeUsage(AttributeTargets.Field)]
public class PropAttribute : Attribute
{
    public PropertyEnum Id;
    public PropAttribute(PropertyEnum id) { Id = id; }
}

